#!/usr/bin/env python

import glob
import os
import optparse
import sys

import charmicat

def main():
    """
    ccat_filter.py [opts] file1.h5 [file2.h5 [...]]

    Apply filters from a module supplied from the command line 
    or inferred from the file.
    """

    op = optparse.OptionParser(usage=main.__doc__)

    op.add_option("-m", "--module", dest='module_name', default=None, 
                  help="Use module MODULE instead of inferring from input", 
                  metavar='MODULE')
    op.add_option("-l", "--logdir", dest='logdir', default=None,
                  help='Specify directory PATH for MP logging', metavar='PATH')
    op.add_option("-o", "--output", dest="output", default=None,
                  help="Place output in directory NAME", metavar='NAME')
    op.add_option("-z", dest='complevel', type='int', default=1,
                  help="Set output compression level 0-9", metavar='0-9')
    op.add_option("--cpus", dest="ncpu", type="int", 
                  default=charmicat.DEFAULT_NCPU,
                  help="Spawn no more than N threads", metavar="N")
    op.add_option("--force-log", dest='force_log', default=False, 
                  action='store_true',
                  help='Force reuse of currently-existing log directory')

    clcc = charmicat.CLCutCollection()

    cutopt_group = optparse.OptionGroup(op, "Cut manipulation options")
    cutopt_group.add_option("-n", dest="by_name", action='callback', 
                            callback=clcc.cb_by_name, default=[], type='string',
                            help="Apply the cut NAME (can be repeated)")
    cutopt_group.add_option("-c", "--cutset", dest="cutsets", 
                            default=[], action='callback', type='string',
                            callback=clcc.cb_cutsets,
                            help="Apply cutset NAME (can be repeated)")
    cutopt_group.add_option("-i", "--intersection", action="callback", 
                            dest="intersection", default=False,
                            callback=clcc.cb_intersect,
                            help="Write output for the intersection " \
                                + "of specified cuts")
    cutopt_group.add_option("-r", "--replace", action="callback", 
                            default=[], callback=clcc.cb_replace, 
                            dest="replace", metavar='CUT1=CUT2', type='string',
                            help="Replace in cutset cut named CUT1 " \
                                + "with cut named CUT2")
    cutopt_group.add_option("-s", dest="by_cl", action='callback', default=[],
                            callback=clcc.cb_by_cl, type='string',
                            help='Apply the cut CUT_EXPR', metavar='CUT_EXPR')
    cutopt_group.add_option("-x", dest='exclude', action='callback', default=[],
                            help="Exclude cut NAME (can be repeated)", 
                            metavar='NAME', callback=clcc.cb_exclude, 
                            type='string')
    cutopt_group.add_option("--start-on", dest="start_on", default=None,
                            action='callback', callback=clcc.cb_start_on,
                            help="Apply only cuts downstream (inclusive)" \
                                + "of NAME" , metavar='NAME', type='string')

    op.add_option_group(cutopt_group)

    
    opts, args = op.parse_args()

    if len(args) == 0:
        msg = "Please supply at least one input file."
        raise charmicat.BadCommandLineError(msg, -1)

    if not opts.module_name:
        opts.module_name = charmicat.module_name_from_paths(args)
    
    try:
        module = __import__(opts.module_name)           
    except ImportError:
        msg = "Error importing module `{0}'".format(opts.module_name)
        raise charmicat.BadCommandLineError(msg, -2)

    cut_list = clcc.cut_list(module)

    args = [os.path.abspath(arg) for arg in args]
    
    if not opts.output:
        if len(opts.by_name) > 0:
            opts.output = os.getcwd()
        elif len(opts.cutsets) > 0:
            opts.output = os.path.abspath('.'.join(opts.cutsets))
        elif len(opts.by_cl) > 0 :
            opts.output = os.path.abspath(' & '.join(opts.by_cl))
        else:
            opts.output = os.path.abspath(module.default_cutset)

    if not os.access(opts.output, os.F_OK):        
        os.mkdir(opts.output)

    os.chdir(opts.output)

    rootdir = os.getcwd()

    if len(args) < opts.ncpu:
        opts.ncpu = len(args)

    for current_cut in cut_list:
        print current_cut.name

#        if not opts.intersection:
        cutdir = os.path.join(os.getcwd(), current_cut.name)        
        os.mkdir(cutdir)
        os.chdir(cutdir)
 #       else:
 #           cutdir = rootdir

        input_lists = charmicat.n_groups(opts.ncpu, args)

        current_cut.prepare(cutdir, opts)

        monitor = charmicat.FilterMonitor(len(input_lists), len(args), 
                                          cutdir, current_cut, opts.logdir)
        workers = [charmicat.FilterWorker(
                monitor.logdir, (current_cut, il, opts)) for il in input_lists]

        charmicat.mp_deploy_and_monitor(monitor, workers, ncpu=opts.ncpu)

        args = current_cut.finalize()

        link_path = os.path.join(rootdir, current_cut.name)
        
        if not os.access(link_path, os.F_OK) and not opts.intersection:
            os.symlink(os.getcwd(), os.path.join(rootdir, current_cut.name))

        if not opts.intersection:
            os.symlink(rootdir, './top')

    return 0


if __name__ == "__main__":
    try:
        sys.exit(main())
    except charmicat.BadCommandLineError, e:
        import os
        sys.stderr.write(str(e) + "\n")
        sys.stderr.write("See `{0} -h' for more.\n".format(os.path.basename(sys.argv[0])))
        sys.stderr.flush()
        sys.exit(e.retcode)
